package com.henu.engineer.service

import com.henu.engineer.dao.entity.cultivatingproject.GraduationAchievedScale
import com.henu.engineer.dao.entity.student.CourseGoalAchieved
import com.henu.engineer.dao.mapper.cultivatingproject.GraduationAchievedScaleXmlMapper
import com.henu.engineer.dao.mapper.cultivatingproject.GraduationRequirementXmlMapper
import com.henu.engineer.dao.mapper.cultivatingproject.IndicatorCourseXmlMapper
import com.henu.engineer.dao.mapper.cultivatingproject.RequirementIndicatorXmlMapper
import com.henu.engineer.dao.mapper.student.*
import com.henu.engineer.dao.mapper.teachingoutline.CourseGoalXmlMapper
import com.henu.engineer.dao.mapper.teachingoutline.CourseMethodXmlMapper
import com.henu.engineer.dao.mapper.testpaper.TestPaperDtlXmlMapper
import com.henu.engineer.dao.mapper.testpaper.TestPaperXmlMapper
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import java.util.ArrayList

/**
 *   达成度一共分为两种：
 * 1. 学生自己的达成度
 *      a. 毕业要求达成度
 *      b. 指标点达成度
 *      c. 课程达成度（也是课程目标总达成度）
 *      d. 课程目标的达成度
 * 2. 所有学生的
 *      a. 毕业要求达成度（所有学生）
 *      b. 指标点达成度
 *      c. 课程达成度（所有学生课程达成度平均值）
 *      d. 课程目标的达成度（所有学生的课程目标达成度平均值）
 *
 * 生成各种达成度的Service
 * @author noexcept
 * @since 9/12/2021 7:49 AM
 */
@Service
class AchieveDegreeGenerateService {

    @Autowired
    lateinit var testPaperXmlMapper: TestPaperXmlMapper

    @Autowired
    lateinit var testPaperDtlXmlMapper: TestPaperDtlXmlMapper

    @Autowired
    lateinit var studentScoreDtlXmlMapper: StudentScoreDtlXmlMapper

    @Autowired
    lateinit var studentScoreXmlMapper: StudentScoreXmlMapper

    @Autowired
    lateinit var courseGoalAchievedXmlMapper: CourseGoalAchievedXmlMapper

    @Autowired
    lateinit var studentCourseAchievedXmlMapper: StudentCourseAchievedXmlMapper

    @Autowired
    lateinit var courseGoalXmlMapper: CourseGoalXmlMapper

    @Autowired
    lateinit var courseMethodXmlMapper: CourseMethodXmlMapper

    @Autowired
    lateinit var studentCourseMethodXmlMapper: StudentCourseMethodXmlMapper

    @Autowired
    lateinit var studentXmlMapper: StudentXmlMapper

    @Autowired
    lateinit var graduationRequirementXmlMapper: GraduationRequirementXmlMapper

    @Autowired
    lateinit var requirementIndicatorXmlMapper: RequirementIndicatorXmlMapper

    @Autowired
    lateinit var graduationAchievedScaleXmlMapper: GraduationAchievedScaleXmlMapper

    @Autowired
    lateinit var indicatorCourseXmlMapper: IndicatorCourseXmlMapper


    /**
     * 生成每个学生的所有毕业要求达成度
     * 存储在graduation-achieve_scale表中
     * @return first:是否成功 second: 错误信息或者影响行数 third：要求达成度列表
     */
    fun generateGraduationAchievedScale(studentId: String): Triple<Boolean, String, List<GraduationAchievedScale>?> {

        var errorMsg = ""

        //1. 把学生对象查出来
        val student = studentXmlMapper.selectStudent(studentId)
        if (student == null) {
            errorMsg = "学号对应的学生不存在"
            return Triple(false, errorMsg, null)
        }

        //查看数据库中是否已有数据
        val graduationAchievedScale = graduationAchievedScaleXmlMapper.selectByStudentId(studentId)
        if (graduationAchievedScale.isNotEmpty()) {
            return Triple(true, "数据库中已存在，未进行运算", graduationAchievedScale)
        }

        //查看当前该生的最新毕业要求版本
        val latestVersionBySpec = graduationRequirementXmlMapper.selectLatestVersionBySpec(student.specialization)

        //查看有哪些毕业要求
        val graduationRequirements = graduationRequirementXmlMapper.selectBySpecAndVersion(
            student.specialization,
            latestVersionBySpec.toString()
        )
        if (graduationRequirements.isEmpty()) {
            errorMsg = "毕业要求为空"
            return Triple(false, errorMsg, null)
        }

        val list = ArrayList<GraduationAchievedScale>(graduationRequirements.size)

        for (requirement in graduationRequirements) {
            //查询每个要求的指标点
            val indicators =
                requirementIndicatorXmlMapper.selectIndicatorByRequirementId(requirement.id)

            //确定指该要求的标点达成度的最小值
            var minIndicatorAS = Double.MAX_VALUE
            for (indicator in indicators) {
                //获取指标点达成度
                val triple = generateIndicatorAchieved(studentId, indicator.indicatorId)
                if (!triple.first) {
                    return Triple(false, "编号为${indicator.indicatorId}的指标点达成度生成失败：${triple.second}", null)
                }
                minIndicatorAS =
                    minIndicatorAS.coerceAtMost(triple.third!!)
            }

            //添加到结果集
            list.add(GraduationAchievedScale(student.id, requirement.id, minIndicatorAS))
        }
        //结果存入数据库
        val effectedRows = graduationAchievedScaleXmlMapper.insertList(list)
        return Triple(true, effectedRows.toString(), list);
    }

    /**
     * 生成某学生自己的指标点的达成度
     * @return first:是否成功 second: 错误信息 third：达成度
     */
    fun generateIndicatorAchieved(studentId: String, indicatorId: Int): Triple<Boolean, String, Double?> {
        //查询指标点的支撑课程有哪些，支撑强度都是多少
        val indicatorCourses = indicatorCourseXmlMapper.selectByIndicatorId(indicatorId)

        var res = 0.0

        for (indicatorCourse in indicatorCourses) {
            //从数据库中查找课程达成度数据
            var scale =
                studentCourseAchievedXmlMapper.selectAchieveScale(studentId, indicatorCourse.courseSysId)
            //如果查不到数据，尝试生成
            if (scale == null) {
                val triple =
                    generateCourseGoalSumAchieved(studentId, indicatorCourse.courseSysId)
                //如果生成失败
                if (!triple.first) {
                    return Triple(false, "${indicatorCourse.courseName}的课程达成度生成错误：${triple.second}", null)
                }
                scale = triple.third
            }
            //加权值求和
            res += scale!! * indicatorCourse.intensity
        }

        return Triple(true, "", res)
    }


    /**
     * 生成某学生的某课程的所有课程目标达成度
     *
     * 只需要每个目标的 计算期末考试考核方式的 分数
     * 每个目标的 其他考核方式的 分数由老师导入 数据来自表student_course_method
     * 生成的数据会导入course_goal_achieved表中
     *
     * @return 是否在数据库中有了数据
     */
    fun generateCourseGoalAchievedScale(
        studentId: String,
        courseId: String
    ): Triple<Boolean, String, List<CourseGoalAchieved>?> {
        //如果数据有了就直接返回
        val existedRes = courseGoalAchievedXmlMapper.selectByStudentIdCourseId(studentId, courseId)
        if (existedRes.isNotEmpty()) {
            return Triple(true, "数据库已有数据，未进行计算", existedRes)
        }
        //确定使用的试卷，查询不到就直接返回
        val testPaper =
            testPaperXmlMapper.selectTestPaperByCourseId(courseId) ?: return Triple(false, "无法确定使用的试卷", null)

        //如果还没试卷成绩，表明还没考试，直接返回
        if (studentScoreXmlMapper.selectByStudentIdTestPaperId(studentId, testPaper.id) == null) {
            return Triple(false, "无法找到试卷成绩", null)
        }
        //查询期末考试的考核方式占比
        val proportion =
            courseMethodXmlMapper.selectProportionByCourseIdMethodName(courseId, "期末考试") ?: return Triple(
                false,
                "无法查到期末考试的考核占比",
                null
            )

        //查询每道题的得分情况
        val list =
            studentScoreDtlXmlMapper.selectByStudentIdTestPaperId(studentId, testPaper.id)


        //查询试卷中的课程目标有那些题对应
        val courseGoalQuestions = testPaperDtlXmlMapper.selectByTestPaperId(testPaper.id).groupBy { it.courseGoalId }
        val paperQuestions = testPaperDtlXmlMapper.selectByTestPaperId(testPaper.id)

        //确定这个课程有哪些目标
        val courseGoalList = courseGoalXmlMapper.selectCourseGoalsByCourseId(courseId)

        //存放计算结果
        val res = HashMap<String, Double>(courseGoalList.size)

        //计算期末考试方面各目标的达成度
        for (i in list.indices) {
            if (!res.containsKey(paperQuestions[i].courseGoalId)) {
                res[paperQuestions[i].courseGoalId] = list[i].score
            } else {
                res[paperQuestions[i].courseGoalId] = res[paperQuestions[i].courseGoalId]!! + list[i].score
            }
        }

        res.forEach { (t, u) ->
            //算出每个目标在期末考试中的满分
            var sum = 0.0
            courseGoalQuestions[t]!!.forEach { sum += it.score }
            res[t] = u / sum * proportion;
        }
        //每个目标的在期末考试方面的达成度计算完毕


        // TODO: 9/22/2021 计算其他方面的达成度加到res中

        //将计算好的数据添加到数据库
        val insertedData = ArrayList<CourseGoalAchieved>()
        res.mapTo(insertedData) {
            CourseGoalAchieved(studentId, null, courseId, null, it.key, "", it.value)
        }
        courseGoalAchievedXmlMapper.insertList(insertedData)

        return Triple(true, "", insertedData)
    }

    /**
     * 生成某学生的课程目标总达成度（课程达成度）
     * @return first:是否成功并存入数据库 second: 错误信息或者影响行数 third：计算的达成度
     */
    fun generateCourseGoalSumAchieved(studentId: String, courseId: String): Triple<Boolean, String, Double?> {
        val scale = studentCourseAchievedXmlMapper.selectAchieveScale(studentId, courseId)
        if (scale != null) {
            return Triple(true, "数据库已有数据，未进行计算", scale)
        }

        var goalAchievedList =
            courseGoalAchievedXmlMapper.selectByStudentIdCourseId(studentId, courseId)
        //如果每个目标的达成度数据还没有
        if (goalAchievedList.isEmpty()) {
            //尝试生成每个目标的达成度
            val triple = generateCourseGoalAchievedScale(studentId, courseId)
            if (!triple.first) {
                return Triple(false, "每个目标的达成度计算出错:${triple.second}", null)
            }
            //重新获取数据
            goalAchievedList = courseGoalAchievedXmlMapper.selectByStudentIdCourseId(studentId, courseId)
        }

        //获取每个目标的占比
        val courseGoalProportionMap = courseGoalXmlMapper.selectCourseGoalsByCourseId(courseId).groupBy { it.id }

        var res = 0.0
        goalAchievedList.forEach {
            res += it.achievedScale * courseGoalProportionMap[it.courseGoalId]!![0].proportion
        }

        //存入数据库
        // FIXME: 10/8/2021 学期还没设置
        val effectedRows = studentCourseAchievedXmlMapper.insertSingle(studentId, courseId, res, "")
        return Triple(true, effectedRows.toString(), res);
    }

    /**
     * 获取某学生的某课程达成度
     */
    @Deprecated("计算错误可能是,暂时别用它", ReplaceWith("generateCourseGoalSumAchieved"), DeprecationLevel.ERROR)
    fun getCourseAchievedScale(studentId: String, courseId: String): Double? {
        var list = courseGoalAchievedXmlMapper.selectByStudentIdCourseId(studentId, courseId)

        if (list.isEmpty()) {
            val triple = generateCourseGoalAchievedScale(studentId, courseId)
            if (!triple.first) {
                return null
            }
            list = courseGoalAchievedXmlMapper.selectByStudentIdCourseId(studentId, courseId)
        }
        return list.sumOf { it.achievedScale } / list.size
    }

    /**
     * 生成 针对于所有学生的每个专业每一届每个毕业要求达成度
     * @param specialization 专业
     * @param grade 表示第几届，格式：YYYY
     * @return first:是否成功 second: 错误信息或者影响行数 third：map:键为毕业要求id,值为达成度
     */
    fun generateGraduationAchievedScaleAllStudent(
        specialization: String,
        grade: String
    ): Triple<Boolean, String, HashMap<Int, Double>?> {

        var map = HashMap<Int, Double>(12)
        val studentList = studentXmlMapper.selectStudentsBySpecAndGrade(specialization, grade)

        //遍历学生列表
        studentList.forEach { it ->
            val triple = generateGraduationAchievedScale(it.id)
            if (!triple.first) {
                return Triple(false, "学生${it.name}(${it.id})的各项毕业要求达成度计算错误：${triple.second}", null)
            }
            val scaleList = triple.third
            //遍历毕业要求列表
            scaleList!!.forEach { gac ->
                if (map.containsKey(gac.graduationRequirementId)) {
                    map[gac.graduationRequirementId] = map[gac.graduationRequirementId]!!.plus(gac.achieveScale)
                } else map[gac.graduationRequirementId] = gac.achieveScale
            }
        }
        //每个毕业要求求平均值
        for (key in map.keys) {
            map[key] = map[key]!!.div(studentList.size)
        }
        return Triple(true, "", map)
    }


    /**
     * 生成  针对于所有学生的课程达成度（所有学生课程达成度平均值）
     * @return first:是否成功 second: 错误信息 third：达成度
     */
    fun generateCourseAchievedScaleAllStudent(
        specialization: String,
        grade: String,
        courseId: String
    ): Triple<Boolean, String, Double?> {

        val studentList = studentXmlMapper.selectStudentsBySpecAndGrade(specialization, grade)
        val res = 0.0

        for (student in studentList) {
            val triple = generateCourseGoalSumAchieved(student.id, courseId)
            if (!triple.first) {
                return Triple(false, "生成错误：${triple.second}", null)
            }
            res.plus(triple.third!!)
        }
        res.div(studentList.size)

        return Triple(true, "", res)
    }

    /**
     * 生成 针对于所有学生的 某课程所有目标的达成度（所有学生的课程目标达成度平均值）
     * @return first: 是否成功 second：错误信息 third：键为课程目标id，值为达成度
     */
    fun generateCourseGoalAchievedAllStudent(
        specialization: String,
        grade: String,
        courseId: String
    ): Triple<Boolean, String, HashMap<String, Double>?> {
        val studentList = studentXmlMapper.selectStudentsBySpecAndGrade(specialization, grade)

        val map = HashMap<String, Double>()

        //遍历学生列表
        for (student in studentList) {
            val triple = generateCourseGoalAchievedScale(student.id, courseId)
            if (!triple.first) {
                return Triple(false, "学生${student.name}(${student.id})课程目标达成度生成出错：${triple.second}", null)
            }
            //遍历课程目标
            val list = triple.third!!
            list.forEach {
                if (map.containsKey(it.courseGoalId)) {
                    map[it.courseGoalId] = map[it.courseGoalId]!!.plus(it.achievedScale)
                } else map[it.courseGoalId] = it.achievedScale
            }
        }

        //每个课程目标的总达成度再平均
        for (key in map.keys) {
            map[key] = map[key]!!.div(studentList.size)
        }

        return Triple(true, "", map);
    }
}